package com.codegeek.project.communicate;

import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.codegeek.common.utils.myStringUtils;
import org.apache.logging.log4j.LogManager;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.ApplicationArguments;
import org.springframework.boot.ApplicationRunner;
import org.springframework.stereotype.Component;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.AsynchronousChannelGroup;
import java.nio.channels.AsynchronousServerSocketChannel;
import java.nio.channels.AsynchronousSocketChannel;
import java.nio.channels.CompletionHandler;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;

/**
 * @author wj
 * @ClassName
 * @Description
 * @date 2020-03-12
 */

@Component
public class SlopeDataCollectService implements ApplicationRunner {

    private final int port=9600;                   //环境服务器默认端口号
    private final int threadSize=50;              //服务器默认线程数
    private final int maxBufferSize=1000;          //默认最大流字节数

    private static final Integer SENSORTYPE_RAIN=5;             //雨量计
    private static final Integer SENSORTYPE_GRADIOGRAPH=3;                 //测斜仪

    private static final Logger _logger = LoggerFactory.getLogger(SlopeDataCollectService.class);
    public static final org.apache.logging.log4j.Logger logger = LogManager.getLogger("FileUtils");

    protected AsynchronousChannelGroup asynchronousChannelGroup;//线程池
    private AsynchronousServerSocketChannel serverChannel;//整个监听服务

    @Autowired
    SlopeSensorHandleService slopeSensorHandleService;

    @Override
    public void run(ApplicationArguments args) throws Exception {
        try{
//            cGDeviceUtil=new CGDeviceUtil(redisUtils,CGRedisEnvirDevice.class);
            asynchronousChannelGroup= AsynchronousChannelGroup.withCachedThreadPool(Executors.newCachedThreadPool(), threadSize);
            serverChannel= AsynchronousServerSocketChannel.open(asynchronousChannelGroup);
            serverChannel.bind(new InetSocketAddress(port));
            System.out.println("数据采集服务启动成功  ，监听端口:"+port);
            serverChannel.accept(this, new CompletionHandler<AsynchronousSocketChannel, SlopeDataCollectService>() {
                @Override
                public void completed(AsynchronousSocketChannel result, SlopeDataCollectService attachment) {
                    try {
                        System.out.println("设备连接到数据采集服务器,设备IP地址:" + result.getRemoteAddress().toString());
                        serverChannel.accept(attachment, this);       // 再次接收客户端连接
                        handleWithCompletionHandler(result);
                    }catch ( Exception e){
                        e.printStackTrace();
                        logger.error(e);
                        throw new RuntimeException(e);
                    }
                }

                @Override
                public void failed(Throwable exc, SlopeDataCollectService attachment) {
                    System.out.println("Received failed");
                    exc.printStackTrace();
                    attachment.serverChannel.accept(attachment, this);
                    logger.error(exc);
                    throw new RuntimeException(exc);
                }

            });
        }catch (IOException e){
            _logger.error(e.toString());
            logger.error(e);
            throw new RuntimeException(e);
        }
    }

    private void handleWithCompletionHandler(final AsynchronousSocketChannel channel){
        try {
            final ByteBuffer buffer = ByteBuffer.allocate(maxBufferSize);
            final long timeout = -1;            //无限等待输入
            channel.read(buffer, timeout, TimeUnit.SECONDS, null, new CompletionHandler<Integer, Object>() {
                @Override
                public void completed(Integer result, Object attachment) {
                    if (result == -1) {//正常断开，断开soceket
                        try {
                            channel.close();
                        } catch (IOException e) {
                            e.printStackTrace();
                            logger.error(e);
                            throw new RuntimeException(e);
                        }
                        return;
                    }
                    buffer.flip();
                    String hexString= myStringUtils.BinaryToHexString(buffer.array());
                    hexString=hexString.replace(" ","");
                    hexString=hexString.substring(0,result*2);
                    _logger.info("收到采集数据："+hexString);
                    buffer.clear();
                    channel.read(buffer, timeout, TimeUnit.SECONDS, null, this);    //继续接收消息
                    handleCollectData(hexString);
                }

                @Override
                public void failed(Throwable exc, Object attachment) {
                    exc.printStackTrace();
                    _logger.error(exc.toString());
                    logger.error(exc);
                    throw new RuntimeException(exc);
                }
            });
        }catch (Exception e){
            e.printStackTrace();
            logger.error(e);
            throw new RuntimeException(e);
        }
    }

    /**
     * @title
     * @description 处理采集数据
     * @param
     * @return
     * @throws
     */
    private void handleCollectData(String hexString){
        try {
            //$$HD -  24244844 协议头
            if (hexString.startsWith("24244844") && hexString.length() >= 22) {
                Integer length = myStringUtils.parseIntFromHexString(hexString.substring(10, 14));
                if (hexString.length() >= 14 + 8 + length * 2) {
                    String info = hexString.substring(14, 14 + length * 2);
                    String crc = hexString.substring(14 + length * 2, 14 + length * 2 + 4);
                    String end = hexString.substring(14 + length * 2 + 4, 14 + length * 2 + 8);
                    //crc校验,协议尾部校验 todo 校验算法有问题，不是简单的CRC直接校验，暂取消校验
//                    if (myStringUtils.buildCrcEx(info).equals(crc) && end.equals("0D0A")) {
                    if (end.equals("0D0A")) {
                        String cmd = info.substring(0, 2);
                        Long sn=myStringUtils.parseLongFromHexString(info.substring(2,10)) ;
                        Long time=myStringUtils.parseLongFromHexString(info.substring(10,22));
                        Date date=new Date(time);
                        SimpleDateFormat sdf=new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
                        String strDate=sdf.format(date);
                        String hexMsg=info.substring(22);
                        String msg= new String(myStringUtils.hexStringToBytes(hexMsg));
                        try{
                            JSONObject jsContent=JSONObject.parseObject(msg);
                            if(jsContent.containsKey("DATA")){
                                JSONArray jsonArray=jsContent.getJSONArray("DATA");
                                for(int i=0;i<jsonArray.size();i++){
                                    JSONObject jsData=jsonArray.getJSONObject(i);
                                    Integer type=jsData.getInteger("type");
                                    Integer addr=jsData.getInteger("addr");
                                    //测斜仪
                                    if(type==SENSORTYPE_GRADIOGRAPH){
                                        Float angleX=jsData.getFloat("Anglex");
                                        Float angleY=jsData.getFloat("Angley");
                                        Float temperature=jsData.getFloat("Temp");
                                        _logger.info("接收到测斜仪数据，Cmd:"+cmd+",sn:"+sn+",时间:"+strDate+",设备地址:"+addr+",Anglex:"+angleX+",Angley:"+angleY+",Temp:"+temperature);
                                        slopeSensorHandleService.pushGradioGraphData(String.valueOf(addr),angleX,angleY,temperature,new Date());
                                    }
                                    //雨量计l
                                    else if(type==SENSORTYPE_RAIN){
                                        Float rainFall=jsData.getFloat("Rainfall");
                                        _logger.info("接收到雨量数据，Cmd:"+cmd+",sn:"+sn+",时间:"+strDate+",设备地址:"+addr+",雨量:"+rainFall);
                                        slopeSensorHandleService.pushRainData(String.valueOf(addr),rainFall,new Date());

                                    }else
                                    {
                                        _logger.info("无法解析的分组数据，Cmd:"+cmd+",sn:"+sn+",时间:"+strDate+",设备地址:"+addr+",数据:"+jsData.toJSONString());
                                    }
                                }
                            }

                        }catch (Exception e){
                            _logger.info("无法解析的设备消息，Cmd:"+cmd+",sn:"+sn+",时间:"+strDate+",消息内容:"+msg);
                            e.printStackTrace();
                            logger.error(e);
                            throw new RuntimeException(e);
                        }
                    }
                }
            } else {
                _logger.info("无法解析的报文:" + hexString);
            }
        }
        catch (Exception e){
            e.printStackTrace();
            logger.error(e);
            throw new RuntimeException(e);
        }
    }

}
